#include "lexer.h"

#include <cstring>
#include <iostream>
#include <vector>
#include <map>

#include "error.h"

using namespace std;

static vector<wstring> keywords = {L"函数", L"类型", L"func", L"type"};

static vector<wstring> operators1 = {
  L"(", L")", L"[", L"]", L"{", L"}", L".", L"+", L"-", L"*", L"/", L"%", L"&", L"|", L"~", L"^", L"!", L"?", L":"
};

static vector<wstring> operators2 = {
  L"//", L"**", L"&&", L"||", L"+=", L"-=", L"*=", L"/=", L"%=", L"&=", L"|=", L"^=", L"==", L"!=", L"++", L"--"
};

static vector<wstring> operators3 = {
  L"//=", L"**=", L"&&=", L"||="
};

static Object True = true;

static Object False = false;

void Lexer::set_code(const std::wstring &code) {
  this->src = code;
  this->pos = 0;
}

Token Lexer::next() {
  if (pos >= src.size())return EOL;

  if (pos + 2 < src.size())
    for (auto &op:operators3) {
      if (op[0] == src[pos] && op[1] == src[pos + 1] && op[2] == src[pos + 2]) {
        return pos += 3, Token(OP, op);
      }
    }

  if (pos + 1 < src.size())
    for (auto &op:operators2) {
      if (op[0] == src[pos] && op[1] == src[pos + 1]) {
        return pos += 2, Token(OP, op);
      }
    }

  for (auto &op:operators1) {
    if (op[0] == src[pos]) return ++pos, Token(OP, op);
  }

  if (src[pos] == L'\n')return pos++, Token(CR, L"\n");
  else if (src[pos] == L' ') {
    wstring s;
    while (pos < src.size() && src[pos] == L' ') s += src[pos++];
    return Token(SPACE, s);
  } else if (src[pos] == L'"' || src[pos] == L'\'')return parse_string();
  else return parse_id_keyword_bool();
}

Token Lexer::parse_string() {
  wchar_t sep = src[pos++];
  if (pos >= src.size()) throw SyntaxError("EOL while scanning string literal.", pos);

  wstring s;
  while (src[pos] != sep) {
    s += src[pos++];
    if (pos >= src.size()) throw SyntaxError("EOL while scanning string literal.", pos);
  }
  pos++;

  return Token(LITERAL, s, new Object(s));
}

Token Lexer::parse_id_keyword_bool() {
  wstring s;
  while (pos < src.size()) {
    wchar_t c = src[pos];
    for (auto op:operators1) if (op[0] == c)goto end_step;
    s += c;
    pos++;
  }

  end_step:
  for (auto &kw:keywords) if (s == kw)return Token(KW, s);
  if (s == L"true" || s == L"真")return Token(LITERAL, s, &True);
  if (s == L"false" || s == L"假")return Token(LITERAL, s, &False);
  return Token(ID, s);
}